home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / Fl_Input_.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-04  |  19.6 KB  |  739 lines

  1. //
  2. // "$Id: Fl_Input_.cxx,v 1.21 1999/03/04 18:09:18 mike Exp $"
  3. //
  4. // Common input widget routines for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-1999 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  24. //
  25.  
  26. // This is the base class for Fl_Input.  You can use it directly
  27. // if you are one of those people who like to define their own
  28. // set of editing keys.  It may also be useful for adding scrollbars
  29. // to the input field.
  30.  
  31. #include <FL/Fl.H>
  32. #include <FL/Fl_Input_.H>
  33. #include <FL/fl_draw.H>
  34. #include <math.h>
  35. #include <string.h>
  36. #include <stdlib.h>
  37. #include <ctype.h>
  38.  
  39. #define MAXBUF 1024
  40.  
  41. ////////////////////////////////////////////////////////////////
  42.  
  43. // Wordwrap kludge.  It "sort of" works but there are lots of bugs.
  44. // Really the whole multi-line-input should be moved to a seperate
  45. // (and much more complex) subclass.  Single-line and multi-line
  46. // input are really a *lot* different!
  47. // To try the wordwrap define this:
  48. //#define WORDWRAP
  49. #ifdef WORDWRAP
  50. static int wordwrap; // width for wordwrapping, should be are to Fl_Input_.
  51. #endif
  52.  
  53. // Copy string p..e to the buffer, replacing characters with ^X and \nnn
  54. // as necessary.  Truncate if necessary so the resulting string and
  55. // null terminator fits in a buffer of size n.  Return new end pointer.
  56. const char* Fl_Input_::expand(const char* p, char* buf) const {
  57.   char* o = buf;
  58.   char* e = buf+(MAXBUF-4);
  59. #ifdef WORDWRAP
  60.   const char* lastspace = p;
  61.   char* lastspace_out = o;
  62.   int width_to_lastspace = 0;
  63. #endif
  64.   if (type()==FL_SECRET_INPUT) {
  65.     while (o<e && p < value_+size_) {*o++ = '*'; p++;}
  66.   } else while (o<e) {
  67. #ifdef WORDWRAP
  68.     if (wordwrap && (p >= value_+size_ || *p==' ' || *p=='\n')) {
  69.       width_to_lastspace += fl_width(lastspace, p-lastspace);
  70.       if (p > lastspace+1) {
  71.     if (width_to_lastspace > wordwrap) {
  72.       p = lastspace; o = lastspace_out; break;
  73.     }
  74.       }
  75.       lastspace = p;
  76.       lastspace_out = o;
  77.     }
  78. #endif
  79.     if (p >= value_+size_) break;
  80.     int c = *p++ & 255;
  81.     if (c < ' ' || c == 127) {
  82.       if (c=='\n' && type()==FL_MULTILINE_INPUT) {p--; break;}
  83.       if (c == '\t' && type()==FL_MULTILINE_INPUT) {
  84.     for (c = (o-buf)%8; c<8 && o<e; c++) *o++ = ' ';
  85.       } else {
  86.     *o++ = '^';
  87.     *o++ = c ^ 0x40;
  88.       }
  89.     } else if (c >= 128 && c < 0xA0) {
  90.       *o++ = '\\';
  91.       *o++ = (c>>6)+'0';
  92.       *o++ = ((c>>3)&7)+'0';
  93.       *o++ = (c&7)+'0';
  94.     } else if (c == 0xA0) { // nbsp
  95.       *o++ = ' ';
  96.     } else {
  97.       *o++ = c;
  98.     }
  99.   }
  100.   *o = 0;
  101.   return p;
  102. }
  103.  
  104. // After filling in such a buffer, find the width to e
  105. double Fl_Input_::expandpos(
  106.   const char* p,    // real string
  107.   const char* e,    // pointer into real string
  108.   const char* buf,    // conversion of real string by expand()
  109.   int* returnn        // return offset into buf here
  110. ) const {
  111.   int n = 0;
  112.   if (type()==FL_SECRET_INPUT) n = e-p;
  113.   else while (p<e) {
  114.     int c = *p++ & 255;
  115.     if (c < ' ' || c == 127) {
  116.       if (c == '\t' && type()==FL_MULTILINE_INPUT) n += 8-(n%8);
  117.       else n += 2;
  118.     } else if (c >= 128 && c < 0xA0) {
  119.       n += 4;
  120.     } else {
  121.       n++;
  122.     }
  123.   }
  124.   if (returnn) *returnn = n;
  125.   return fl_width(buf, n);
  126. }
  127.  
  128. ////////////////////////////////////////////////////////////////
  129.  
  130. // minimal update:
  131. // Characters from mu_p to end of widget are redrawn.
  132. // If erase_cursor_only, small part at mu_p is redrawn.
  133. // Right now minimal update just keeps unchanged characters from
  134. // being erased, so they don't blink.
  135.  
  136. void Fl_Input_::minimal_update(int p) {
  137.   if (damage() & FL_DAMAGE_ALL) return; // don't waste time if it won't be done
  138.   if (damage() & FL_DAMAGE_EXPOSE) {
  139.     if (p < mu_p) mu_p = p;
  140.   } else {
  141.     mu_p = p;
  142.   }
  143.   damage(FL_DAMAGE_EXPOSE);
  144.   erase_cursor_only = 0;
  145. }
  146.  
  147. void Fl_Input_::minimal_update(int p, int q) {
  148.   if (q < p) p = q;
  149.   minimal_update(p);
  150. }
  151.  
  152. ////////////////////////////////////////////////////////////////
  153.  
  154. static double up_down_pos;
  155. static int was_up_down;
  156.  
  157. void Fl_Input_::setfont() const {
  158.  fl_font(textfont(), textsize());
  159. }
  160.  
  161. void Fl_Input_::drawtext(int X, int Y, int W, int H) {
  162.  
  163.   int do_mu = !(damage()&FL_DAMAGE_ALL);
  164.   if (Fl::focus()!=this && !size()) {
  165.     if (do_mu) { // we have to erase it if cursor was there
  166.       fl_color(color());
  167.       fl_rectf(X, Y, W, H);
  168.     }
  169.     return;
  170.   }
  171.  
  172.   int selstart, selend;
  173.   if (Fl::focus()!=this && Fl::selection_owner()!=this && Fl::pushed()!=this)
  174.     selstart = selend = 0;
  175.   else if (position() <= mark()) {
  176.     selstart = position(); selend = mark();
  177.   } else {
  178.     selend = position(); selstart = mark();
  179.   }
  180.  
  181.   setfont();
  182. #ifdef WORDWRAP
  183.   if (type()==FL_MULTILINE_INPUT) wordwrap = W; else wordwrap = 0;
  184. #endif
  185.  
  186.   const char *p, *e;
  187.   char buf[MAXBUF];
  188.  
  189.   // count how many lines and put the last one into the buffer:
  190.   // And figure out where the cursor is:
  191.   int height = fl_height();
  192.   int lines;
  193.   int curx, cury;
  194.   for (p=value(), curx=cury=lines=0; ;) {
  195.     e = expand(p, buf);
  196.     if (position() >= p-value() && position() <= e-value()) {
  197.       curx = int(expandpos(p, value()+position(), buf, 0)+.5);
  198.       if (Fl::focus()==this && !was_up_down) up_down_pos = curx;
  199.       cury = lines*height;
  200.       if (Fl::focus()==this) {
  201.     int fullw = int(expandpos(p, e, buf, 0));
  202.     if (curx > xscroll_+W-20) {
  203.       xscroll_ = curx+20-W;
  204.       if (xscroll_ > fullw-W+2) xscroll_ = fullw-W+2;
  205.       mu_p = 0; erase_cursor_only = 0;
  206.     }
  207.     if (curx < xscroll_+20 && xscroll_) {
  208.       if (fullw > W-2) xscroll_ = curx-20;
  209.       else xscroll_ = 0;
  210.       mu_p = 0; erase_cursor_only = 0;
  211.     }
  212.     if (xscroll_ < 0) xscroll_ = 0;
  213.       }
  214.     }
  215.     lines++;
  216.     if (e >= value_+size_) break;
  217.     if (*e == '\n' || *e == ' ') e++;
  218.     p = e;
  219.   }
  220.  
  221.   // adjust the scrolling:
  222.   if (type()==FL_MULTILINE_INPUT) {
  223.     int newy = yscroll_;
  224.     if (cury < newy) newy = cury;
  225.     if (cury > newy+H-height) newy = cury-H+height;
  226.     if (newy < -1) newy = -1;
  227.     if (newy != yscroll_) {yscroll_ = newy; mu_p = 0; erase_cursor_only = 0;}
  228.   } else {
  229.     yscroll_ = -(H-height)/2;
  230.   }
  231.  
  232.   fl_clip(X, Y, W, H);
  233.   Fl_Color color = active_r() ? textcolor() : inactive(textcolor());
  234.  
  235.   p = value();
  236.   // visit each line and draw it:
  237.   int desc = height-fl_descent();
  238.   int ypos = -yscroll_;
  239.   for (; ypos < H;) {
  240.  
  241.     // re-expand line unless it is the last one calculated above:
  242.     if (lines>1) e = expand(p, buf);
  243.  
  244.     if (ypos <= -height) goto CONTINUE; // clipped off top
  245.  
  246.     if (do_mu) {    // for minimal update:
  247.       const char* pp = value()+mu_p; // pointer to where minimal update starts
  248.       if (e >= pp && (!erase_cursor_only || p <= pp)) { // we must erase this
  249.     // calculate area to erase:
  250.     int x1 = -xscroll_;
  251.     if (p < pp) x1 += int(expandpos(p, pp, buf, 0));
  252.     // erase it:
  253.     fl_color(this->color());
  254.     fl_rectf(X+x1, Y+ypos, erase_cursor_only?2:W-x1, height);
  255.     // it now draws entire line over it
  256.     // this should not draw letters to left of erased area, but
  257.     // that is nyi.
  258.       }
  259.     }
  260.  
  261.     // Draw selection area if required:
  262.     if (selstart < selend && selstart <= e-value() && selend > p-value()) {
  263.       const char* pp = value()+selstart;
  264.       int x1 = -xscroll_;
  265.       int offset1 = 0;
  266.       if (pp > p) {
  267.     fl_color(color);
  268.     x1 += int(expandpos(p, pp, buf, &offset1));
  269.     fl_draw(buf, offset1, X-xscroll_, Y+ypos+desc);
  270.       }
  271.       pp = value()+selend;
  272.       int x2 = W;
  273.       int offset2;
  274.       if (pp <= e) x2 = int(expandpos(p, pp, buf, &offset2))-xscroll_;
  275.       else offset2 = strlen(buf);
  276.       fl_color(selection_color());
  277.       fl_rectf(X+int(x1+.5), Y+ypos, int(x2-x1), height);
  278.       fl_color(contrast(textcolor(), selection_color()));
  279.       fl_draw(buf+offset1, offset2-offset1, X+x1, Y+ypos+desc);
  280.       if (pp < e) {
  281.     fl_color(color);
  282.     fl_draw(buf+offset2, X+x2, Y+ypos+desc);
  283.       }
  284.     } else {
  285.       // draw the cursor:
  286.       if (Fl::focus() == this && selstart == selend &&
  287.       position() >= p-value() && position() <= e-value()) {
  288.     fl_color(cursor_color());
  289.     fl_rectf(X+curx-xscroll_, Y+ypos, 2, height);
  290.       }
  291.       fl_color(color);
  292.       fl_draw(buf, X-xscroll_, Y+ypos+desc);
  293.     }
  294.   CONTINUE:
  295.     ypos += height;
  296.     if (e >= value_+size_) break;
  297.     if (*e == '\n' || *e == ' ') e++;
  298.     p = e;
  299.   }
  300.  
  301.   // for minimal update, erase all lines below last one if necessary:
  302.   if (type()==FL_MULTILINE_INPUT && do_mu && ypos<H
  303.       && (!erase_cursor_only || p <= value()+mu_p)) {
  304.     if (ypos < 0) ypos = 0;
  305.     fl_color(this->color());
  306.     fl_rectf(X, Y+ypos, W, H-ypos);
  307.   }
  308.  
  309.   fl_pop_clip();
  310. }
  311.  
  312. static int isword(char c) {
  313.   return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c));
  314. }
  315.  
  316. int Fl_Input_::wordboundary(int i) const {
  317.   if (i<=0 || i>=size()) return 1;
  318.   return isword(index(i-1)) != isword(index(i));
  319. }
  320.  
  321. int Fl_Input_::lineboundary(int i) const {
  322.   if (i<=0 || i>=size()) return 1;
  323.   if (type() != FL_MULTILINE_INPUT) return 0;
  324.   return index(i-1) == '\n' || index(i) == '\n';
  325. }
  326.  
  327. void Fl_Input_::handle_mouse(int X, int Y,
  328. #ifdef WORDWRAP
  329.                  int W,
  330. #else
  331.                  int /*W*/,
  332. #endif
  333.                  int /*H*/, int drag) {
  334.   was_up_down = 0;
  335.   if (!size()) return;
  336.   setfont();
  337.  
  338.   const char *p, *e;
  339.   char buf[MAXBUF];
  340.  
  341.   int theline = (type()==FL_MULTILINE_INPUT) ?
  342.     (Fl::event_y()-Y+yscroll_)/fl_height() : 0;
  343.  
  344.   int newpos = 0;
  345. #ifdef WORDWRAP
  346.   if (type()==FL_MULTILINE_INPUT) wordwrap = W; else wordwrap = 0;
  347. #endif
  348.   for (p=value();; ) {
  349.     e = expand(p, buf);
  350.     theline--; if (theline < 0) break;
  351.     if (*e == '\n' || *e == ' ') e++;
  352.     p = e;
  353.     if (e >= value_+size_) break;
  354.   }
  355.   const char *l, *r, *t;
  356.   for (l = p, r = e; l<r; ) {
  357.     double f;
  358.     t = l+(r-l+1)/2;
  359.     f = X-xscroll_+expandpos(p, t, buf, 0);
  360.     if (f <= Fl::event_x()) l = t;
  361.     else r = t-1;
  362.   }
  363.   newpos = l-value();
  364.  
  365.   int newmark = drag ? mark() : newpos;
  366.   if (Fl::event_clicks()) {
  367.     if (newpos >= newmark) {
  368.       if (newpos == newmark) {
  369.     if (newpos < size()) newpos++;
  370.     else newmark--;
  371.       }
  372.       if (Fl::event_clicks()>1 || type()==FL_SECRET_INPUT) {
  373.     while (!lineboundary(newpos)) newpos++;
  374.     while (!lineboundary(newmark)) newmark--;
  375.       } else {
  376.     while (!wordboundary(newpos)) newpos++;
  377.     while (!wordboundary(newmark)) newmark--;
  378.       }
  379.     } else {
  380.       if (Fl::event_clicks()>1 || type()==FL_SECRET_INPUT) {
  381.     while (!lineboundary(newpos)) newpos--;
  382.       } else {
  383.     while (!wordboundary(newpos)) newpos--;
  384.       }
  385.     }
  386.   }
  387.   position(newpos, newmark);
  388. }
  389.  
  390. int Fl_Input_::position(int p, int m) {
  391.   was_up_down = 0;
  392.   if (p<0) p = 0;
  393.   if (p>size()) p = size();
  394.   if (m<0) m = 0;
  395.   if (m>size()) m = size();
  396.   if (p == position_ && m == mark_) return 0;
  397.   if (Fl::selection_owner() == this) Fl::selection_owner(0);
  398.   if (p != m) {
  399.     if (p != position_) minimal_update(position_, p);
  400.     if (m != mark_) minimal_update(mark_, m);
  401.   } else {
  402.     // new position is a cursor
  403.     if (position_ == mark_) {
  404.       // old position was just a cursor
  405.       if (Fl::focus() == this && !(damage()&FL_DAMAGE_EXPOSE)) {
  406.     minimal_update(position_); erase_cursor_only = 1;
  407.       }
  408.     } else { // old position was a selection
  409.       minimal_update(position_, mark_);
  410.     }
  411.   }
  412.   position_ = p;
  413.   mark_ = m;
  414.   return 1;
  415. }
  416.  
  417. int Fl_Input_::up_down_position(int i, int keepmark) {
  418.   while (i > 0 && index(i-1) != '\n') i--;    // go to start of line
  419.   double oldwid = 0.0;
  420.   setfont();
  421.   while (index(i) && index(i)!='\n') {
  422.     double tt = oldwid + fl_width(index(i));
  423.     if ((oldwid+tt)/2 >= up_down_pos) break;
  424.     oldwid = tt;
  425.     i++;
  426.   }
  427.   int j = position(i, keepmark ? mark_ : i);
  428.   was_up_down = 1;
  429.   return j;
  430. }
  431.  
  432. int Fl_Input_::copy() {
  433.   if (mark() != position()) {
  434.     int b, e; if (position() < mark()) {
  435.       b = position(); e = mark();
  436.     } else {
  437.       e = position(); b = mark();
  438.     }
  439.     Fl::selection(*this, value()+b, (type()!=FL_SECRET_INPUT) ? e-b : 0);
  440.     return 1;
  441.   }
  442.   return 0;
  443. }
  444.  
  445. #define MAXFLOATSIZE 40
  446.  
  447. static char* undobuffer;
  448. static int undobufferlength;
  449. static Fl_Input_* undowidget;
  450. static int undoat;    // points after insertion
  451. static int undocut;    // number of characters deleted there
  452. static int undoinsert;    // number of characters inserted
  453. static int yankcut;    // length of valid contents of buffer, even if undocut=0
  454.  
  455. static void undobuffersize(int n) {
  456.   if (n > undobufferlength) {
  457.     if (undobuffer) {
  458.       do {undobufferlength *= 2;} while (undobufferlength < n);
  459.       undobuffer = (char*)realloc(undobuffer, undobufferlength);
  460.     } else {
  461.       undobufferlength = n+9;
  462.       undobuffer = (char*)malloc(undobufferlength);
  463.     }
  464.   }
  465. }
  466.  
  467. // all changes go through here, delete characters b-e and insert text:
  468. int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
  469.  
  470.   was_up_down = 0;
  471.  
  472.   if (b<0) b = 0;
  473.   if (e<0) e = 0;
  474.   if (b>size_) b = size_;
  475.   if (e>size_) e = size_;
  476.   if (e<b) {int t=b; b=e; e=t;}
  477.   if (text && !ilen) ilen = strlen(text);
  478.   if (e<=b && !ilen) return 0; // don't clobber undo for a null operation
  479.   if (size_+ilen-(e-b) > maximum_size_) {
  480.     ilen = maximum_size_-size_+(e-b);
  481.     if (ilen < 0) ilen = 0;
  482.   }
  483.  
  484.   put_in_buffer(size_+ilen);
  485.  
  486.   if (e>b) {
  487.     if (undowidget == this && b == undoat) {
  488.       undobuffersize(undocut+(e-b));
  489.       memcpy(undobuffer+undocut, value_+b, e-b);
  490.       undocut += e-b;
  491.     } else if (undowidget == this && e == undoat && !undoinsert) {
  492.       undobuffersize(undocut+(e-b));
  493.       memmove(undobuffer+(e-b), undobuffer, undocut);
  494.       memcpy(undobuffer, value_+b, e-b);
  495.       undocut += e-b;
  496.     } else if (undowidget == this && e == undoat && (e-b)<undoinsert) {
  497.       undoinsert -= e-b;
  498.     } else {
  499.       undobuffersize(e-b);
  500.       memcpy(undobuffer, value_+b, e-b);
  501.       undocut = e-b;
  502.       undoinsert = 0;
  503.     }
  504.     memmove(buffer+b, buffer+e, size_-e+1);
  505.     size_ -= e-b;
  506.     undowidget = this;
  507.     undoat = b;
  508.     if (type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut;
  509.   }
  510.  
  511.   if (ilen) {
  512.     if (undowidget == this && b == undoat)
  513.       undoinsert += ilen;
  514.     else {
  515.       undocut = 0;
  516.       undoinsert = ilen;
  517.     }
  518.     memmove(buffer+b+ilen, buffer+b, size_-b+1);
  519.     memcpy(buffer+b, text, ilen);
  520.     size_ += ilen;
  521.   }
  522.   undowidget = this;
  523.   mark_ = position_ = undoat = b+ilen;
  524.  
  525.   minimal_update(b);
  526.   if (when()&FL_WHEN_CHANGED) do_callback(); else set_changed();
  527.   return 1;
  528. }
  529.  
  530. int Fl_Input_::undo() {
  531.   was_up_down = 0;
  532.   if (undowidget != this || !undocut && !undoinsert) return 0;
  533.  
  534.   int ilen = undocut;
  535.   int xlen = undoinsert;
  536.   int b = undoat-xlen;
  537.   int b1 = b;
  538.  
  539.   put_in_buffer(size_+ilen);
  540.  
  541.   if (ilen) {
  542.     memmove(buffer+b+ilen, buffer+b, size_-b+1);
  543.     memcpy(buffer+b, undobuffer, ilen);
  544.     size_ += ilen;
  545.     b += ilen;
  546.   }
  547.  
  548.   if (xlen) {
  549.     undobuffersize(xlen);
  550.     memcpy(undobuffer, buffer+b, xlen);
  551.     memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1);
  552.     size_ -= xlen;
  553.   }
  554.  
  555.   undocut = xlen;
  556.   if (xlen) yankcut = xlen;
  557.   undoinsert = ilen;
  558.   undoat = b;
  559.   mark_ = b /* -ilen */;
  560.   position_ = b;
  561.  
  562.   minimal_update(b1);
  563.   if (when()&FL_WHEN_CHANGED) do_callback(); else set_changed();
  564.   return 1;
  565. }
  566.  
  567. #if 0
  568. int Fl_Input_::yank() {
  569.   // fake yank by trying to get it out of undobuffer
  570.   if (!yankcut) return 0;
  571.   return change(position(), position(), undobuffer, yankcut);
  572. }
  573. #endif
  574.  
  575. int Fl_Input_::copy_cuts() {
  576.   // put the yank buffer into the X clipboard
  577.   if (!yankcut) return 0;
  578.   Fl::selection(*this, undobuffer, yankcut);
  579.   return 1;
  580. }
  581.  
  582. void Fl_Input_::maybe_do_callback() {
  583.   if (changed() || (when()&FL_WHEN_NOT_CHANGED)) {
  584.     clear_changed(); do_callback();}
  585. }
  586.  
  587. int Fl_Input_::handletext(int event, int X, int Y, int W, int H) {
  588.   switch (event) {
  589.  
  590.   case FL_FOCUS:
  591.     if (mark_ == position_) {
  592.       minimal_update(size()+1);
  593.     } else if (Fl::selection_owner() != this)
  594.       minimal_update(mark_, position_);
  595.     return 1;
  596.  
  597.   case FL_UNFOCUS:
  598.     if (mark_ == position_) {
  599.       if (!(damage()&FL_DAMAGE_EXPOSE)) {minimal_update(position_); erase_cursor_only = 1;}
  600.     } else if (Fl::selection_owner() != this) {
  601.       minimal_update(mark_, position_);
  602.     }
  603.     if (when() & FL_WHEN_RELEASE) maybe_do_callback();
  604.     return 1;
  605.  
  606.   case FL_PUSH:
  607.     handle_mouse(X, Y, W, H, Fl::event_state(FL_SHIFT));
  608.     return 1;
  609.  
  610.   case FL_DRAG:
  611.     handle_mouse(X, Y, W, H, 1);
  612.     return 1;
  613.  
  614.   case FL_RELEASE:
  615. //  handle_mouse(X, Y, W, H, 1);
  616.     copy();
  617.     return 1;
  618.  
  619.   case FL_SELECTIONCLEAR:
  620.     minimal_update(mark_, position_);
  621.     mark_ = position_;
  622.     return 1;
  623.  
  624.   case FL_PASTE: {
  625.     // strip trailing control characters and spaces before pasting:
  626.     const char* t = Fl::event_text();
  627.     const char* e = t+Fl::event_length();
  628.     if (type()!=FL_MULTILINE_INPUT) while (e > t && *(uchar*)(e-1) <= ' ') e--;
  629.     return replace(position(), mark(), t, e-t);}
  630.  
  631.   default:
  632.     return 0;
  633.   }
  634. }
  635.  
  636. /*------------------------------*/
  637.  
  638. Fl_Input_::Fl_Input_(int x, int y, int w, int h, const char* l)
  639. : Fl_Widget(x, y, w, h, l) {
  640.   box(FL_DOWN_BOX);
  641.   color(FL_WHITE, FL_SELECTION_COLOR);
  642.   align(FL_ALIGN_LEFT);
  643.   textsize_ = FL_NORMAL_SIZE;
  644.   textfont_ = FL_HELVETICA;
  645.   textcolor_ = FL_BLACK;
  646.   cursor_color_ = FL_BLACK; // was FL_BLUE
  647.   mark_ = position_ = size_ = 0;
  648.   bufsize = 0;
  649.   buffer  = 0;
  650.   value_ = "";
  651.   xscroll_ = yscroll_ = 0;
  652.   maximum_size_ = 32767;
  653. }
  654.  
  655. void Fl_Input_::put_in_buffer(int len) {
  656.   if (value_ == buffer && bufsize > len) {
  657.     buffer[size_] = 0;
  658.     return;
  659.   }
  660.   if (!bufsize) {
  661.     if (len > size_) len += 9; // let a few characters insert before realloc
  662.     bufsize = len+1; 
  663.     buffer = (char*)malloc(bufsize);
  664.   } else if (bufsize <= len) {
  665.     // we may need to move old value in case it points into buffer:
  666.     int moveit = (value_ >= buffer && value_ < buffer+bufsize);
  667.     // enlarge current buffer
  668.     if (len > size_) {
  669.       do {bufsize *= 2;} while (bufsize <= len);
  670.     } else {
  671.       bufsize = len+1;
  672.     }
  673.     char* nbuffer = (char*)realloc(buffer, bufsize);
  674.     if (moveit) value_ += (nbuffer-buffer);
  675.     buffer = nbuffer;
  676.   }
  677.   memmove(buffer, value_, size_); buffer[size_] = 0;
  678.   value_ = buffer;
  679. }
  680.  
  681. int Fl_Input_::static_value(const char* str, int len) {
  682.   clear_changed();
  683.   if (undowidget == this) undowidget = 0;
  684.   if (str == value_ && len == size_) return 0;
  685.   if (len) { // non-empty new value:
  686.     if (xscroll_ || yscroll_) {
  687.       xscroll_ = yscroll_ = 0;
  688.       minimal_update(0);
  689.     } else {
  690.       int i = 0;
  691.       // find first different character:
  692.       if (value_) {
  693.     for (; i<size_ && i<len && str[i]==value_[i]; i++);
  694.     if (i==size_ && i==len) return 0;
  695.       }
  696.       minimal_update(i);
  697.     }
  698.     value_ = str;
  699.     size_ = len;
  700.   } else { // empty new value:
  701.     if (!size_) return 0; // both old and new are empty.
  702.     size_ = 0;
  703.     value_ = "";
  704.     xscroll_ = yscroll_ = 0;
  705.     minimal_update(0);
  706.   }
  707.   position(size(), 0);
  708.   return 1;
  709. }
  710.  
  711. int Fl_Input_::static_value(const char* str) {
  712.   return static_value(str, str ? strlen(str) : 0);
  713. }
  714.  
  715. int Fl_Input_::value(const char* str, int len) {
  716.   int r = static_value(str, len);
  717.   if (len) put_in_buffer(len);
  718.   return r;
  719. }
  720.  
  721. int Fl_Input_::value(const char* str) {
  722.   return value(str, str ? strlen(str) : 0);
  723. }
  724.  
  725. void Fl_Input_::resize(int X, int Y, int W, int H) {
  726.   if (W != w()) xscroll_ = 0;
  727.   if (H != h()) yscroll_ = 0;
  728.   Fl_Widget::resize(X, Y, W, H);
  729. }
  730.  
  731. Fl_Input_::~Fl_Input_() {
  732.   if (undowidget == this) undowidget = 0;
  733.   if (bufsize) free((void*)buffer);
  734. }
  735.  
  736. //
  737. // End of "$Id: Fl_Input_.cxx,v 1.21 1999/03/04 18:09:18 mike Exp $".
  738. //
  739.